package com.scope.tasks;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.media.ThumbnailUtils;
import android.os.AsyncTask;
import android.os.Handler;
import android.util.Log;

import com.scope.R;
import com.scope.customViews.ImageButton;

import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Created by Samrith on 11/2/13.
 */
public class ImageLoader {
    public ImageLoader(Context context) {
        mContext = context;
    }

    private Context mContext;

    private static final String LOG_TAG = "FileLoader";

    /**
     * Returns true if the current load has been canceled or if there was no load in
     * progress on this image view.
     * Returns false if the load in progress deals with the same url. The load is not
     * stopped in that case.
     */
    private static boolean cancelPotentialDownload(String url, ImageButton imageButton) {
        BitmapLoaderTask bitmapLoaderTask = getBitmapLoaderTask(imageButton);

        if (bitmapLoaderTask != null) {
            String bitmapUrl = bitmapLoaderTask.url;
            if ((bitmapUrl == null) || (!bitmapUrl.equals(url))) {
                bitmapLoaderTask.cancel(true);
            } else {
                // The same URL is already being downloaded.
                return false;
            }
        }
        return true;
    }

    /**
     * Download the specified image from the Internet and binds it to the provided ImageRotatedView. The
     * binding is immediate if the image is found in the cache and will be done asynchronously
     * otherwise. A null bitmap will be associated to the ImageRotatedView if an error occurs.
     *
     * @param filePath    The URL of the image to load.
     * @param imageButton The ImageRotatedView to bind the downloaded image to.
     */
    public void load(String filePath, ImageButton imageButton) {
        resetPurgeTimer();
        Drawable bitmap = getBitmapFromCache(filePath);

        if (bitmap == null) {
            BitmapLoaderTask task = new BitmapLoaderTask(imageButton);
            LoadedDrawable loadedDrawable = new LoadedDrawable(task);
            imageButton.setImageDrawable(loadedDrawable);
            task.execute(filePath);
        } else {
            cancelPotentialDownload(filePath, imageButton);
            imageButton.setImageDrawable(bitmap);
        }
    }

    /**
     * @param imageButton Any imageButton
     * @return Retrieve the currently active load task (if any) associated with this imageButton.
     * null if there is no such task.
     */
    private static BitmapLoaderTask getBitmapLoaderTask(ImageButton imageButton) {
        if (imageButton != null) {
            Drawable drawable = imageButton.getDrawable();
            if (drawable instanceof LoadedDrawable) {
                LoadedDrawable loadedDrawable = (LoadedDrawable) drawable;
                return loadedDrawable.getBitmapDownloaderTask();
            }
        }
        return null;
    }

    protected Drawable loadBitmap(String url) {
        return new BitmapDrawable(mContext.getResources(),
                ThumbnailUtils.extractThumbnail(
                    BitmapFactory.decodeFile(url),
                    mContext.getResources().getDimensionPixelSize(R.dimen.button_width_v_m),
                    mContext.getResources().getDimensionPixelSize(R.dimen.button_height_v_m))
        );
    }

    /**
     * The actual AsyncTask that will asynchronously load the image.
     */
    class BitmapLoaderTask extends AsyncTask<String, Void, Drawable> {
        private String url;
        private final WeakReference<ImageButton> imageViewReference;

        public BitmapLoaderTask(ImageButton imageButton) {
            imageViewReference = new WeakReference<ImageButton>(imageButton);
        }

        /**
         * Actual load method.
         */
        @Override
        protected Drawable doInBackground(String... params) {
            url = params[0];
            return loadBitmap(url);
        }

        /**
         * Once the image is downloaded, associates it to the imageView
         */
        @Override
        protected void onPostExecute(Drawable bitmap) {
            if (isCancelled()) {
                bitmap = null;
            }

            addBitmapToCache(url, bitmap);

            if (imageViewReference != null) {
                ImageButton imageButton = imageViewReference.get();
                BitmapLoaderTask bitmapLoaderTask = getBitmapLoaderTask(imageButton);
                if (this == bitmapLoaderTask) {
                    imageButton.setImageDrawable(bitmap);
                }
            }
        }
    }


    /**
     * A fake Drawable that will be attached to the imageView while the load is in progress.
     * <p/>
     * <p>Contains a reference to the actual load task, so that a load task can be stopped
     * if a new binding is required, and makes sure that only the last started load process can
     * bind its result, independently of the load finish order.</p>
     */
    static class LoadedDrawable extends ColorDrawable {
        private final WeakReference<BitmapLoaderTask> LoaderTaskReference;

        public LoadedDrawable(BitmapLoaderTask bitmapLoaderTask) {
            super(Color.LTGRAY);
            LoaderTaskReference =
                    new WeakReference<BitmapLoaderTask>(bitmapLoaderTask);
        }

        public BitmapLoaderTask getBitmapDownloaderTask() {
            return LoaderTaskReference.get();
        }
    }

    /*
     * Cache-related fields and methods.
     *
     * We use a hard and a soft cache. A soft reference cache is too aggressively cleared by the
     * Garbage Collector.
     */

    private static final int HARD_CACHE_CAPACITY = 10;
    private static final int DELAY_BEFORE_PURGE = 10 * 1000; // in milliseconds

    // Hard cache, with a fixed maximum capacity and a life duration
    private final HashMap<String, Drawable> sHardBitmapCache =
            new LinkedHashMap<String, Drawable>(HARD_CACHE_CAPACITY / 2, 0.75f, true) {
                @Override
                protected boolean removeEldestEntry(LinkedHashMap.Entry<String, Drawable> eldest) {
                    if (size() > HARD_CACHE_CAPACITY) {
                        // Entries push-out of hard reference cache are transferred to soft reference cache
                        sSoftBitmapCache.put(eldest.getKey(), new SoftReference<Drawable>(eldest.getValue()));
                        return true;
                    } else
                        return false;
                }
            };

    // Soft cache for bitmaps kicked out of hard cache
    private final static ConcurrentHashMap<String, SoftReference<Drawable>> sSoftBitmapCache =
            new ConcurrentHashMap<String, SoftReference<Drawable>>(HARD_CACHE_CAPACITY / 2);

    private final Handler purgeHandler = new Handler();

    private final Runnable purger = new Runnable() {
        public void run() {
            clearCache();
        }
    };

    /**
     * Adds this bitmap to the cache.
     *
     * @param bitmap The newly downloaded bitmap.
     */
    private void addBitmapToCache(String url, Drawable bitmap) {
        if (bitmap != null) {
            synchronized (sHardBitmapCache) {
                sHardBitmapCache.put(url, bitmap);
            }
        }
    }

    /**
     * @param url The URL of the image that will be retrieved from the cache.
     * @return The cached bitmap or null if it was not found.
     */
    private Drawable getBitmapFromCache(String url) {
        // First try the hard reference cache
        synchronized (sHardBitmapCache) {
            final Drawable bitmap = sHardBitmapCache.get(url);
            if (bitmap != null) {
                // Bitmap found in hard cache
                // Move element to first position, so that it is removed last
                sHardBitmapCache.remove(url);
                sHardBitmapCache.put(url, bitmap);
                return bitmap;
            }
        }

        // Then try the soft reference cache
        SoftReference<Drawable> bitmapReference = sSoftBitmapCache.get(url);
        if (bitmapReference != null) {
            final Drawable bitmap = bitmapReference.get();
            if (bitmap != null) {
                // Bitmap found in soft cache
                return bitmap;
            } else {
                // Soft reference has been Garbage Collected
                sSoftBitmapCache.remove(url);
            }
        }

        return null;
    }

    /**
     * Clears the image cache used internally to improve performance. Note that for memory
     * efficiency reasons, the cache will automatically be cleared after a certain inactivity delay.
     */
    public void clearCache() {
        sHardBitmapCache.clear();
        sSoftBitmapCache.clear();
    }

    /**
     * Allow a new delay before the automatic cache clear is done.
     */
    private void resetPurgeTimer() {
        purgeHandler.removeCallbacks(purger);
        purgeHandler.postDelayed(purger, DELAY_BEFORE_PURGE);
    }
}


//
//
//public enum Mode { NO_ASYNC_TASK, NO_LOADED_FILE, CORRECT }
//private Mode mode = Mode.NO_ASYNC_TASK;
//
//    public void setMode(Mode mode) {
//        this.mode = mode;
//        clearCache();
//    }
//
//    /*
//     * Same as load but the image is always downloaded and the cache is not used.
//     * Kept private at the moment as its interest is not clear.
//       private void forceLoad(String url, ImageRotatedView view) {
//          forceLoad(url, view, null);
//       }
//     */
//
//    /**
//     * Same as load but the image is always downloaded and the cache is not used.
//     * Kept private at the moment as its interest is not clear.
//     */
//    private void forceLoad(String url, ImageButton imageButton) {
//        // State sanity: url is guaranteed to never be null in LoadedDrawable and cache keys.
//        if (url == null) {
//            imageButton.setImageDrawable(null);
//            return;
//        }
//
//        if (cancelPotentialDownload(url, imageButton)) {
//            switch (mode) {
//                case NO_ASYNC_TASK:
//                    Bitmap bitmap = loadBitmap(url);
//                    addBitmapToCache(url, bitmap);
//                    imageButton.setImageBitmap(bitmap);
//                    break;
//
//                case NO_LOADED_FILE:
//                    imageButton.setMinimumHeight(156);
//                    BitmapLoaderTask task = new BitmapLoaderTask(imageButton);
//                    task.execute(url);
//                    break;
//
//                case CORRECT:
//                    task = new BitmapLoaderTask(imageButton);
//                    LoadedDrawable loadedDrawable = new LoadedDrawable(task);
//                    imageButton.setImageDrawable(loadedDrawable);
//                    task.execute(url);
//                    break;
//            }
//        }
//    }



